﻿using System;
using System.Collections.Generic;
using System.IO.Ports;
using System.Diagnostics;
using System.Linq;
using System.Threading;

namespace nhCore
{
    public class SPIO : IO
    {
        /// <summary>
        /// 建立SPIO对象
        /// </summary>
        /// <param name="com">多COM口字符串</param>
        /// <param name="br">波特率</param>
        /// <param name="parity">效验位</param>
        public SPIO(string com, int br, int parity)
        {
            TimeOut = 120;
            string[] coms = com.Split(','); //字符串按","分割为字符串数组
            SpConfigs = new List<SP>();

            //SP sp;
            foreach (string c in coms)
            {
                SP sp = new SP(c)
                {
                    BaudRate = br
                };
                switch (parity)
                {
                    case 1:
                        sp.StopBits = StopBits.One;
                        sp.Parity = Parity.Odd;
                        break;
                    case 2:
                        sp.StopBits = StopBits.One;
                        sp.Parity = Parity.Even;
                        break;
                    case 3:
                        sp.StopBits = StopBits.One;
                        sp.Parity = Parity.None;
                        break;
                    case 4:
                        sp.StopBits = StopBits.Two;
                        sp.Parity = Parity.None;
                        break;
                    default:
                        sp.StopBits = StopBits.Two;
                        sp.Parity = Parity.None;
                        break;
                }
                SpConfigs.Add(sp);
            }

            SerialPorts = new List<SerialPort>();
            Name = "串口";
        }

        public List<SP> SpConfigs { get; set; }

        private List<SerialPort> SerialPorts { get; set; }

        /// <summary>
        /// 关闭服务
        /// </summary>
        /// <returns></returns>
        public override bool Close()
        {
            if (IsOpened)
            {
                IsOpened = false;
                lock (SerialPorts)
                {
                    foreach (SerialPort sp in SerialPorts)
                    {
                        if (sp.IsOpen)
                        {
                            sp.Close();
                            IOListener.DisConnected(this, sp);
                        }
                    }
                }
            }
            else
            {
                Debug.WriteLine("串口已经关闭，不需要再次关闭");
            }

            return true;
        }

        /// <summary>
        /// 打开串口
        /// </summary>
        /// <returns></returns>
        public override bool Open(bool asyncComm = false)
        {
            bool result = false;
            SerialPort sp;
            string err = "";
            foreach (SP s in SpConfigs)
            {
                sp = new SerialPort
                {
                    PortName = s.PortName,
                    BaudRate = s.BaudRate,
                    StopBits = s.StopBits,
                    Parity = s.Parity,
                    ReadTimeout = s.ReadTimeout,
                    WriteTimeout = s.WriteTimeout,
                };
                SerialPorts.Add(sp);
                try
                {
                    sp.Open();

                    if (asyncComm)
                    {   //异步通讯，串口数据接受事件委托给自定义的方法处理
                        sp.DataReceived += new SerialDataReceivedEventHandler(Received);
                    }
                    result = true;
                    IOListener.Connected(this, sp);
                }
                catch (Exception)
                {
                    err += $"{s.PortName}接口打开失败。";
                }
            }
            IsOpened = result;
            if (!IsOpened) { err += "所有COM口均已关闭。"; }
            if (!string.IsNullOrEmpty(err))
            {
                IOListener.Error(err);
            }
            return result;
        }

        public override object SendAccept(object client, object writeData)
        {
            SerialPort sp = (SerialPort)client;
            byte[] readData = null;
            lock (sp)
            {
                if (sp.IsOpen)
                {
                    try
                    {
                        byte []writeDatas = writeData as byte[];
                        sp.Write(writeDatas, 0, writeDatas.Length);

                        Thread.Sleep(TimeOut);
                        int s = sp.BytesToRead;
                        if (s > 0)
                        {
                            readData = new byte[s];
                            _ = sp.Read(readData, 0, s);//接受为0时不会有异常
                        }
                        else
                        {
                            //Debug.WriteLine($"字节数：{s}");
                        }
                    }
                    catch (Exception e)
                    {
                        Debug.WriteLine(e);
                        IOListener.DisConnected(this, client);
                        if (SerialPorts.Where(s => s.IsOpen == true).FirstOrDefault() == null)
                        {   //没有COM口打开则停止服务
                            IsOpened = false;
                        }
                    }
                }
            }
            return readData;
        }

        /// <summary>
        /// 串口有数据
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Received(object sender, SerialDataReceivedEventArgs e)
        {
            Thread.Sleep(TimeOut);
            if (e.EventType == SerialData.Eof) { return; }
            else
            {
                SerialPort sp = (SerialPort)sender;
                byte[] buffer;
                int len = sp.BytesToRead;
                if (len > 0)
                {
                    buffer = new byte[len];
                    _ = sp.Read(buffer, 0, len);//接受为0时不会有异常
                    IOListener.Received(this, sp, buffer);
                }
                else
                {
                    IOListener.DisConnected(this, sp);
                    lock (SerialPorts)
                    {
                        if (SerialPorts.Where(s => s.IsOpen == true).FirstOrDefault() == null)
                        {   //所有串口都断开了
                            IsOpened = false;
                        }
                    }
                }
            }
        }

        public override bool Send(object client, in object writeData)
        {
            SerialPort sp = (SerialPort)client;
            lock (sp)
            {
                if (sp.IsOpen)
                {
                    try
                    {
                        byte[] writeDatas = writeData as byte[];
                        sp.Write(writeDatas, 0, writeDatas.Length);
                    }
                    catch (Exception)
                    {
                        IOListener.DisConnected(this, client);
                        if (SerialPorts.Where(s => s.IsOpen == true).FirstOrDefault() == null)
                        {   //没有COM口打开则停止服务
                            IsOpened = false;
                        }
                    }
                }
            }
            return true;
        }

        /// <summary>
        /// 串口
        /// </summary>
        public class SP
        {
            private string _PortName;

            /// <summary>
            /// 构造函数
            /// </summary>
            /// <param name="portName">串口号</param>
            /// <param name="baudRate">波特率</param>
            /// <param name="stopBits">停止位</param>
            public SP(string portName, int baudRate = 9600, StopBits stopBits = StopBits.Two)
            {
                PortName = portName;
                BaudRate = baudRate;
                StopBits = stopBits;
                ReadTimeout = -1;
                WriteTimeout = 100;
            }

            /// <summary>
            /// 串口名称
            /// </summary>
            public string PortName
            {
                get => _PortName;
                set
                {
                    if (string.IsNullOrEmpty(value))
                    {
                        throw new ArgumentException("无串口名称。");
                    }
                    else if (value.Trim().ToUpper().StartsWith("") && value.Trim().Length > 3)
                    {
                        _PortName = value;
                    }
                    else { throw new InvalidOperationException("无效的COM。"); }
                    _PortName = value;
                }
            }

            /// <summary>
            /// 波特率
            /// </summary>
            public int BaudRate { get; set; }

            /// <summary>
            /// 停止位
            /// </summary>
            public StopBits StopBits { get; set; }

            /// <summary>
            /// 校验位
            /// </summary>
            public Parity Parity { get; set; }

            /// <summary>
            /// 读取超时
            /// </summary>
            public int ReadTimeout { get; set; }

            /// <summary>
            /// 写入超时
            /// </summary>
            public int WriteTimeout { get; set; }

        }
    }
}
